package com.joshua.spring.service.impl;

import com.joshua.spring.entity.dto.JobAddDTO;
import com.joshua.spring.entity.dto.JobInstanceDTO;
import com.joshua.spring.entity.dto.JobKeyDTO;
import com.joshua.spring.entity.dto.JobUpdateDTO;
import com.joshua.spring.entity.em.JobOptTypeEnum;
import com.joshua.spring.entity.em.ResponseEnum;
import com.joshua.spring.exception.MicroServiceException;
import com.joshua.spring.service.inter.JobCtrlService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static com.joshua.spring.constants.SymbolConstants.EMPTY_STR;

/**
 * projectName dragon-cloud
 * package com.joshua.spring.service.inter
 * classname  JobCtrlServiceImpl
 * <p>
 * description
 * </p>
 *
 * @author <a href="mailto:joshualwork@163.com">joshua_liu</a>
 * @date 2020/9/13 9:47
 */
@Slf4j
@Service
public class JobCtrlServiceImpl implements JobCtrlService {

    @Autowired
    private Scheduler scheduler;

    @PostConstruct
    public void startScheduler() {
        try {
            scheduler.start();
        } catch (SchedulerException e) {
            log.error("Catch scheduler exception when scheduler start", e);
        }
    }

    /**
     * description: 通过时间增加一个job
     *
     * @param dto {@link JobAddDTO}
     * @return {@link String}
     * @throws MicroServiceException 微服务公共异常
     * @author <a href="mailto:joshualwork@163.com">joshua_liu</a>
     * @date 2020/5/14 22:04
     */
    @Override
    public String addJobByJobTime(JobAddDTO dto) {
        try {
            // 任务名称和组构成任务key
            JobDetail jobDetail = JobBuilder.newJob(dto.getJobClass())
                    .withIdentity(dto.getJobName(), dto.getJobGroupName())
                    .build();
            // 设置job参数
            if (!CollectionUtils.isEmpty(dto.getJobData())) {
                jobDetail.getJobDataMap().putAll(dto.getJobData());
            }
            // 使用simpleTrigger规则
            Trigger trigger = null;
            if (dto.getJobTimes() < 0) {
                trigger = TriggerBuilder.newTrigger().withIdentity(dto.getJobName(), dto.getJobGroupName())
                        .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(dto.getJobTime()))
                        .startNow().build();
            } else {
                trigger = TriggerBuilder.newTrigger().withIdentity(dto.getJobName(), dto.getJobGroupName())
                        .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(dto.getJobTime())
                                .withRepeatCount(dto.getJobTimes())).startNow().build();
            }
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            log.error("Catch scheduler exception when add job", e);
            throw MicroServiceException.builder(ResponseEnum.JOB_FAIL, e.getMessage());
        }
        return dto.getJobName();
    }

    /**
     * description: 通过corn表达式 增加一个job
     *
     * @param dto {@link JobAddDTO}
     * @return {@link String}
     * @throws MicroServiceException 微服务公共异常
     * @author <a href="mailto:joshualwork@163.com">joshua_liu</a>
     * @date 2020/5/14 22:04
     */
    @Override
    public String addJobByCorn(JobAddDTO dto) {
        try {
            // 创建jobDetail实例，绑定Job实现类 ,指明job的名称，所在组的名称，以及绑定job类  任务名称和组构成任务key
            JobDetail jobDetail = JobBuilder.newJob(dto.getJobClass()).withIdentity(dto.getJobName(), dto.getJobGroupName()).build();
            // 设置job参数
            if (!CollectionUtils.isEmpty(dto.getJobData())) {
                jobDetail.getJobDataMap().putAll(dto.getJobData());
            }
            // 定义调度触发规则 使用cornTrigger规则 触发器key
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(dto.getJobName(), dto.getJobGroupName())
                    .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
                    .withSchedule(CronScheduleBuilder.cronSchedule(dto.getCorn())).startNow().build();
            // 把作业和触发器注册到任务调度中
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {
            log.error("Catch scheduler exception when add job", e);
            throw MicroServiceException.builder(ResponseEnum.JOB_FAIL, e.getMessage());
        }
        return dto.getJobName();
    }

    /**
     * description: 修改 一个job的 执行频率
     *
     * @param dto
     * @return {@link String}
     * @throws MicroServiceException 微服务公共异常
     * @author <a href="mailto:joshualwork@163.com">joshua_liu</a>
     * @date 2020/5/14 22:16
     */
    @Override
    public String updateJobCornInfo(JobUpdateDTO dto) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(dto.getJobName(), dto.getJobGroupName());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                    .withSchedule(CronScheduleBuilder.cronSchedule(dto.getCorn())).build();
            // 重启触发器
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            log.error("Catch scheduler exception when update job corn info", e);
            throw MicroServiceException.builder(ResponseEnum.JOB_FAIL, e.getMessage());
        }
        return dto.getJobName();
    }

    /**
     * description: 操作Job
     *
     * @param jobKeyDTO
     * @param optType
     * @return {@link String}
     * @throws MicroServiceException 微服务公共异常
     * @author <a href="mailto:joshualwork@163.com">joshua_liu</a>
     * @date 2020/5/15 9:13
     */
    @Override
    public String optJob(JobKeyDTO jobKeyDTO, String optType) {
        try {
            JobKey jobKey = JobKey.jobKey(jobKeyDTO.getJobName(), jobKeyDTO.getJobGroupName());
            JobOptTypeEnum optTypeEnum = JobOptTypeEnum.getInstanceByCode(optType);
            switch (optTypeEnum) {
                case DELETE:
                    scheduler.deleteJob(jobKey);
                    break;
                case PAUSE:
                    scheduler.pauseJob(jobKey);
                    break;
                case RESUME:
                    scheduler.resumeJob(jobKey);
                    break;
                case TRIGGER:
                    scheduler.triggerJob(jobKey);
                    break;
                default:
                    throw MicroServiceException.builder(ResponseEnum.JOB_FAIL, "Job opt type not found");
            }
        } catch (MicroServiceException sde) {
            throw sde;
        } catch (Exception e) {
            throw MicroServiceException.builder(ResponseEnum.JOB_FAIL, e.getMessage());
        }
        return jobKeyDTO.getJobName();
    }

    /**
     * description: 获取所有计划中的任务列表
     *
     * @return {@link List<JobInstanceDTO>}
     * @throws MicroServiceException 微服务公共异常
     * @author <a href="mailto:joshualwork@163.com">joshua_liu</a>
     * @date 2020/5/14 20:20
     */
    @Override
    public List<JobInstanceDTO> queryAllJob() {
        List<JobInstanceDTO> jobList = null;
        try {
            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            jobList = new ArrayList<>();
            for (JobKey jobKey : jobKeys) {
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    jobList.add(jobEntityFactory(trigger, jobKey));
                }
            }
        } catch (SchedulerException e) {
            log.error("Catch scheduler exception when query all job info", e);
            throw MicroServiceException.builder(ResponseEnum.JOB_FAIL, e.getMessage());
        }
        return jobList;
    }

    /**
     * description: 获取所有正在运行的job
     *
     * @return {@link List<JobInstanceDTO>}
     * @throws MicroServiceException 微服务公共异常
     * @author <a href="mailto:joshualwork@163.com">joshua_liu</a>
     * @date 2020/5/14 20:31
     */
    @Override
    public List<JobInstanceDTO> queryRunJob() {
        List<JobInstanceDTO> jobList = null;
        try {
            List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
            jobList = new ArrayList<>(executingJobs.size());
            for (JobExecutionContext executingJob : executingJobs) {
                JobDetail jobDetail = executingJob.getJobDetail();
                Trigger trigger = executingJob.getTrigger();
                JobKey jobKey = jobDetail.getKey();
                jobList.add(jobEntityFactory(trigger, jobKey));
            }
        } catch (SchedulerException e) {
            log.error("Catch scheduler exception when query all job info", e);
            throw MicroServiceException.builder(ResponseEnum.JOB_FAIL, e.getMessage());
        }
        return jobList;
    }

    /**
     * description: 封装
     *
     * @param trigger
     * @param jobKey
     * @return {@link JobInstanceDTO}
     * @throws {@link SchedulerException}
     * @author <a href="mailto:joshualwork@163.com">joshua_liu</a>
     * @date 2020/5/14 21:12
     */
    private JobInstanceDTO jobEntityFactory(Trigger trigger, JobKey jobKey) throws SchedulerException {
        String descriptionFormatter = "触发器:%s";
        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
        String cronExpression = trigger instanceof CronTrigger ? ((CronTrigger) trigger).getCronExpression() : EMPTY_STR;
        return JobInstanceDTO.builder().jobName(jobKey.getName()).jobGroupName(jobKey.getGroup())
                .description(String.format(descriptionFormatter, trigger.getKey())).jobStatus(triggerState.name())
                .corn(cronExpression).build();
    }

}
